#include <bits/stdc++.h>
using namespace std;

/*
1498. 满足条件的子序列数目
已解答
中等
相关标签
premium lock icon
相关企业
提示
给你一个整数数组 nums 和一个整数 target 。

请你统计并返回 nums 中能满足其最小元素与最大元素的 和 小于或等于 target 的 非空 子序列的数目。

由于答案可能很大，请将结果对 109 + 7 取余后返回。

 

示例 1：

输入：nums = [3,5,6,7], target = 9
输出：4
解释：有 4 个子序列满足该条件。
[3] -> 最小元素 + 最大元素 <= target (3 + 3 <= 9)
[3,5] -> (3 + 5 <= 9)
[3,5,6] -> (3 + 6 <= 9)
[3,6] -> (3 + 6 <= 9)
示例 2：

输入：nums = [3,3,6,8], target = 10
输出：6
解释：有 6 个子序列满足该条件。（nums 中可以有重复数字）
[3] , [3] , [3,3], [3,6] , [3,6] , [3,3,6]
示例 3：

输入：nums = [2,3,3,4,6,7], target = 12
输出：61
解释：共有 63 个非空子序列，其中 2 个不满足条件（[6,7], [7]）
有效序列总数为（63 - 2 = 61）
 

提示：

1 <= nums.length <= 105
1 <= nums[i] <= 106
1 <= target <= 106
*/


// 法一
class Solution {
public:
    using ll = long long;
    const ll MOD = 1e9 + 7;
    int numSubseq(vector<int>& nums, int target) {
        int n = nums.size();
        // 这里是先排序 后面双指针可以操作
        sort(nums.begin(), nums.end());

        // 2^i mod MOD
        vector<int> power(n);
        power[0] = 1;
        for (int i = 1; i < n; i++) {
            power[i] = (power[i - 1] * 2) % MOD;
        }

        int ans = 0;
        int left = 0, right = n - 1;
        // 双指针遍历  最小元素与最大元素的 和 小于或等于 target
        // nums[left] + nums[right] <= target
        while (left <= right) {
            // 大于的情况就压缩右侧空间 右指针就左移
            if (nums[left] + nums[right] > target)      right--;
            else {
                // 以nums[left]为最小值的所有子序列数目为2^(right-left)
                ans = (ans + power[right - left]) % MOD;
                // 迭代尝试 左指针就右移
                left++;
            }
        }

        return ans;
    }
};

// 法二
const int MOD = 1e9 + 7;
const int MAX_N = 1e5 + 5;

int f[MAX_N];

// 计算所有2的幂次
auto _ = []() {
    f[0] = 1;
    for (int i = 1; i < MAX_N; ++i)
        f[i] = ((long long)f[i - 1] << 1) % MOD;
    return 0;
}();

class Solution {
  public:
    int numSubseq(vector<int>& nums, int target) {
        int length = nums.size();
        int result = 0;

        // 统计每个元素的出现次数
        int max_num = *max_element(nums.begin(), nums.end());
        vector<int> cnt(max_num + 1);
        for (int num : nums)
            ++cnt[num];

        int left = 0, right = max_num, mid = length;
        while (left <= right) {
            if (cnt[left] == 0) {
                ++left;
                continue;
            }

            if (cnt[right] == 0) {
                --right;
                continue;
            }

            int sum = left + right;
            if (sum <= target) {
                // 有效 子序列数目 (2^count[left] - 1) * 2^  剩余数目
                result = (result + (f[cnt[left]] - 1) * (long long)f[mid - cnt[left]]) % MOD;
                mid -= cnt[left++];
            } else
                mid -= cnt[right--];
        }
        return result;
    }
};
